package com.tiro.springboot.sample.model;

import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;

import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.Objects;

/**
 * @author long.qul
 * @date 23/01/2018
 */

public class ModelObject implements Serializable {

    private static final long serialVersionUID = 7261544746491030257L;

    public static final String EMPTY_OBJECT_LITERAL = "{}";

    public static final String EMPTY_ARRAY_LITERAL = "[]";

    public static final String NULL_LITERAL = "null";

    public static final ObjectMapper OBJECT_MAPPER = new ObjectMapper()
        .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
        .configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true)
        .configure(JsonParser.Feature.ALLOW_NUMERIC_LEADING_ZEROS, true)
        .configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true)
        .configure(SerializationFeature.WRITE_NULL_MAP_VALUES, false)
        .setSerializationInclusion(Include.NON_NULL);
    // JS 和 Java 的 Date 函数默认的序列化和反序列化都以 yyyy-MM-dd'T'HH:mm:ss.SSS'Z 进行；另外，不建议前端做特殊处理。
    //.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));


    public static <T> T readNullableValue(String str, Class<T> type) {
        try {
            return OBJECT_MAPPER.readValue(str, type);
        } catch (IOException e) {
            return null;
        }
    }

    public static <T> T readValueOrDefault(String str, TypeReference type, T defaultValue) {
        T value = readNullableValue(str, type);
        return Objects.isNull(value) ? defaultValue : value;
    }

    public static <T> T readNullableValue(String str, TypeReference type) {
        try {
            if(str != null) {
                return OBJECT_MAPPER.readValue(str, type);
            }else{
                return null;
            }
        } catch (IOException e) {
            return null;
        }
    }

    public static String writeNullableObject(Object object) {
        return writeNullable(object, EMPTY_OBJECT_LITERAL, false);
    }

    public static String writeNullableCollection(Collection col) {
        return writeNullable(col, EMPTY_ARRAY_LITERAL, false);
    }

    private static String writeNullable(Object obj, String defaultStr, Boolean pretty) {
        if (obj == null) {
            return NULL_LITERAL;
        }
        try {
            if (pretty) {
                return OBJECT_MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
            }
            return OBJECT_MAPPER.writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            return defaultStr;
        }
    }

    @Override
    public String toString() {
        String fields = writeNullableObject(this);
        return String.format(
            "<%s@%s id=%s> JSON: %s",
            this.getClass().getName(),
            System.identityHashCode(this),
            this.getIdString(), fields
        );
    }

    public String toJson() {
        return writeNullable(this, EMPTY_OBJECT_LITERAL, true);
    }


    private Object getIdString() {
        try {
            Field idField = this.getClass().getDeclaredField("id");
            return idField.get(this);
        } catch (SecurityException e) {
            return "";
        } catch (NoSuchFieldException e) {
            return "";
        } catch (IllegalArgumentException e) {
            return "";
        } catch (IllegalAccessException e) {
            return "";
        }
    }
}